package com.hxb.example.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.hxb.example.entity.XxlJobGroup;
import com.hxb.example.entity.XxlJobInfo;
import com.hxb.example.entity.XxlJobLog;
import com.hxb.example.exception.XxlJobException;
import com.hxb.example.model.LogResult;
import com.hxb.example.model.ReturnT;
import com.hxb.example.service.XxlJobGroupService;
import com.hxb.example.service.XxlJobInfoService;
import com.hxb.example.service.XxlJobLogService;
import com.hxb.example.util.I18nUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.HtmlUtils;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * index controller
 *
 * @author xuxueli 2015-12-19 16:13:16
 */
@Slf4j
@Controller
@RequestMapping("/joblog")
public class JobLogController {

    @Resource
    private XxlJobGroupService xxlJobGroupService;
    @Resource
    public XxlJobInfoService xxlJobInfoService;
    @Resource
    public XxlJobLogService xxlJobLogService;

    @RequestMapping
    public String index(HttpServletRequest request, Model model, @RequestParam(required = false, defaultValue = "0") Integer jobId) {

        // 执行器列表
        List<XxlJobGroup> jobGroupList_all = xxlJobGroupService.findAll();

        // filter group
        List<XxlJobGroup> jobGroupList = JobInfoController.filterJobGroupByRole(request, jobGroupList_all);
        if (jobGroupList == null || jobGroupList.isEmpty()) {
            throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
        }

        model.addAttribute("JobGroupList", jobGroupList);

        // 任务
        if (jobId > 0) {
            XxlJobInfo jobInfo = xxlJobInfoService.getById(jobId);
            if (jobInfo == null) {
                throw new RuntimeException(I18nUtil.getString("jobinfo_field_id") + I18nUtil.getString("system_unvalid"));
            }

            model.addAttribute("jobInfo", jobInfo);

            // valid permission
            JobInfoController.validPermission(request, jobInfo.getJobGroup());
        }

        return "joblog/joblog.index";
    }

    @RequestMapping("/getJobsByGroup")
    @ResponseBody
    public ReturnT<List<XxlJobInfo>> getJobsByGroup(int jobGroup) {
        List<XxlJobInfo> list = xxlJobInfoService.list(QueryWrapper.create().eq(XxlJobInfo::getJobGroup, jobGroup));
        return new ReturnT<>(list);
    }

    @RequestMapping("/pageList")
    @ResponseBody
    public Map<String, Object> pageList(HttpServletRequest request,
                                        @RequestParam(required = false, defaultValue = "0") int start,
                                        @RequestParam(required = false, defaultValue = "10") int length,
                                        int jobGroup, int jobId, int logStatus, String filterTime) {

        // valid permission
        JobInfoController.validPermission(request, jobGroup);    // 仅管理员支持查询全部；普通用户仅支持查询有权限的 jobGroup

        // parse param
        Date triggerTimeStart = null;
        Date triggerTimeEnd = null;
        if (filterTime != null && !filterTime.trim().isEmpty()) {
            String[] temp = filterTime.split(" - ");
            if (temp.length == 2) {
                triggerTimeStart = DateUtil.parseDateTime(temp[0]);
                triggerTimeEnd = DateUtil.parseDateTime(temp[1]);
            }
        }
        Page<XxlJobLog> page = xxlJobLogService.page(Page.of(start / length + 1, length),
                QueryWrapper.create().eq(XxlJobLog::getJobGroup, jobGroup, jobId == 0 && jobGroup > 0)
                        .eq(XxlJobLog::getJobId, jobId, jobId > 0)
                        .ge(XxlJobLog::getTriggerTime, triggerTimeStart, triggerTimeStart != null)
                        .le(XxlJobLog::getTriggerTime, triggerTimeEnd, triggerTimeEnd != null)
                        .eq(XxlJobLog::getHandleCode, 200, logStatus == 1)
                        .and(queryWrapper -> {
                            queryWrapper.notIn(XxlJobLog::getTriggerCode, Arrays.asList(0, 200))
                                    .or(wrapper -> {
                                        wrapper.notIn(XxlJobLog::getHandleCode, Arrays.asList(0, 200));
                                    });
                        }, logStatus == 2)
                        .and(queryWrapper -> {
                            queryWrapper.eq(XxlJobLog::getTriggerCode, 200)
                                    .eq(XxlJobLog::getHandleCode, 0);
                        }, logStatus == 3)
                        .orderBy(XxlJobLog::getTriggerTime, false));

        // package result
        Map<String, Object> maps = new HashMap<String, Object>();
        maps.put("recordsTotal", page.getTotalRow());        // 总记录数
        maps.put("recordsFiltered", page.getTotalRow());    // 过滤后的总记录数
        maps.put("data", page.getRecords());                    // 分页列表
        return maps;
    }

    @RequestMapping("/logDetailPage")
    public String logDetailPage(int id, Model model) {

        // base check
        ReturnT<String> logStatue = ReturnT.SUCCESS;
        XxlJobLog jobLog = xxlJobLogService.getById(id);
        if (jobLog == null) {
            throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid"));
        }

        model.addAttribute("triggerCode", jobLog.getTriggerCode());
        model.addAttribute("handleCode", jobLog.getHandleCode());
        model.addAttribute("logId", jobLog.getId());
        return "joblog/joblog.detail";
    }

    @RequestMapping("/logDetailCat")
    @ResponseBody
    public ReturnT<LogResult> logDetailCat(long logId, int fromLineNum) {
        try {
            // valid
            XxlJobLog jobLog = xxlJobLogService.getById(logId);    // todo, need to improve performance
            if (jobLog == null) {
                return new ReturnT<LogResult>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_logid_unvalid"));
            }

            // log cat
//            ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(jobLog.getExecutorAddress());
//            ReturnT<LogResult> logResult = executorBiz.log(new LogParam(jobLog.getTriggerTime().getTime(), logId, fromLineNum));
            ReturnT<LogResult> logResult = null;
            // is end
            if (logResult.getContent() != null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {
                if (jobLog.getHandleCode() > 0) {
                    logResult.getContent().setEnd(true);
                }
            }

            // fix xss
            if (logResult.getContent() != null && StringUtils.hasText(logResult.getContent().getLogContent())) {
                String newLogContent = logResult.getContent().getLogContent();
                newLogContent = HtmlUtils.htmlEscape(newLogContent, "UTF-8");
                logResult.getContent().setLogContent(newLogContent);
            }

            return logResult;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return new ReturnT<>(ReturnT.FAIL_CODE, e.getMessage());
        }
    }

    @RequestMapping("/logKill")
    @ResponseBody
    public ReturnT<String> logKill(int id) {
        // base check
        XxlJobLog log = xxlJobLogService.getById(id);
        XxlJobInfo jobInfo = xxlJobInfoService.getById(log.getJobId());
        if (jobInfo == null) {
            return new ReturnT<>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
        }
        if (ReturnT.SUCCESS_CODE != log.getTriggerCode()) {
            return new ReturnT<>(500, I18nUtil.getString("joblog_kill_log_limit"));
        }

        // request of kill
        ReturnT<String> runResult = null;
//        try {
//            ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(log.getExecutorAddress());
//            runResult = executorBiz.kill(new KillParam(jobInfo.getId()));
//        } catch (Exception e) {
//            logger.error(e.getMessage(), e);
//            runResult = new ReturnT<String>(500, e.getMessage());
//        }

        if (ReturnT.SUCCESS_CODE == runResult.getCode()) {
            log.setHandleCode(ReturnT.FAIL_CODE);
            log.setHandleMsg(I18nUtil.getString("joblog_kill_log_byman") + ":" + (runResult.getMsg() != null ? runResult.getMsg() : ""));
            log.setHandleTime(LocalDateTime.now());
//            XxlJobCompleter.updateHandleInfoAndFinish(log);
            return new ReturnT<>(runResult.getMsg());
        } else {
            return new ReturnT<>(500, runResult.getMsg());
        }
    }

    @RequestMapping("/clearLog")
    @ResponseBody
    public ReturnT<String> clearLog(int jobGroup, int jobId, int type) {

        LocalDateTime clearBeforeTime = null;
        int clearBeforeNum = 0;
        if (type == 1) {
            // 清理一个月之前日志数据
            clearBeforeTime = LocalDateTimeUtil.offset(LocalDateTime.now(), -1, ChronoUnit.DAYS);
        } else if (type == 2) {
            // 清理三个月之前日志数据
            clearBeforeTime = LocalDateTimeUtil.offset(LocalDateTime.now(), -3, ChronoUnit.MONTHS);
        } else if (type == 3) {
            // 清理六个月之前日志数据
            clearBeforeTime = LocalDateTimeUtil.offset(LocalDateTime.now(), -6, ChronoUnit.MONTHS);
        } else if (type == 4) {
            // 清理一年之前日志数据
            clearBeforeTime = LocalDateTimeUtil.offset(LocalDateTime.now(), -1, ChronoUnit.YEARS);
        } else if (type == 5) {
            // 清理一千条以前日志数据
            clearBeforeNum = 1000;
        } else if (type == 6) {
            // 清理一万条以前日志数据
            clearBeforeNum = 10000;
        } else if (type == 7) {
            // 清理三万条以前日志数据
            clearBeforeNum = 30000;
        } else if (type == 8) {
            // 清理十万条以前日志数据
            clearBeforeNum = 100000;
        } else if (type == 9) {
            // 清理所有日志数据
            clearBeforeNum = 0;
        } else {
            return new ReturnT<>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_clean_type_unvalid"));
        }

        xxlJobLogService.remove(QueryWrapper.create()
                .eq(XxlJobLog::getJobGroup, jobGroup, jobGroup > 0)
                .eq(XxlJobLog::getJobId, jobId, jobId > 0)
                .le(XxlJobLog::getTriggerTime, clearBeforeTime, Objects.nonNull(clearBeforeTime))
                .notIn(XxlJobLog::getId,
                        QueryWrapper.create().select(XxlJobLog::getId)
                                .eq(XxlJobLog::getJobGroup, jobGroup, jobGroup > 0)
                                .eq(XxlJobLog::getJobId, jobId, jobId > 0)
                                .orderBy(XxlJobLog::getTriggerTime, false)
                                .limit(clearBeforeNum, 0)
                        , clearBeforeNum > 0)
        );

//        List<Long> logIds = null;
//        do {
//            logIds = xxlJobLogDao.findClearLogIds(jobGroup, jobId, clearBeforeTime, clearBeforeNum, 1000);
//            if (logIds != null && logIds.size() > 0) {
//                xxlJobLogDao.clearLog(logIds);
//            }
//        } while (logIds != null && logIds.size() > 0);

        return ReturnT.SUCCESS;
    }

}
